﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Windows.Forms;

namespace MarioMapEditor
{
    public partial class MainForm : Form
    {
        public static int MapMaxWitdh = 340;
        public static int MapMaxHeight= 100;
        public static int TileSize = 16;
        public static int FileSize = 16;

        public struct MapObject
        {
            public int ID;
            public int Info;
        }

        private List<ObObject> _backgroundMap;
        private List<ObObject> _chosedMap;
        //
        private ObObject _addObject;
        private ObObject _selectedObject;

        private int _row, _col;
        private string _fileName;
        private Graphics _g;

        public MainForm()
        {
            InitializeComponent();

            _addObject = new ObObject();
            _selectedObject = new ObObject();
            pbMouse.Parent = panel1;

            tabObject.Visible   = false;

            //pbMain.SendToBack();
            pbMain.Parent = panel1;
            pbStuff.Parent = pbMain;
            pbStuff.BackColor = Color.Transparent;

            //=================================

            ObObject temp = new ObBrick();
            pbBrick.Image       = Properties.Resources.Brick;
            pbBrick.Size        = new Size(TileSize * temp.GetWidth()*3, TileSize * temp.GetHeight()*3);

            temp = new ObBrickSP();
            pbBrickSP.Image = Properties.Resources.BrickSP;
            pbBrickSP.Size = new Size(TileSize * temp.GetWidth() * 3, TileSize * temp.GetHeight() * 3);

            pbScoreTree.Image = Properties.Resources.ScoreTree;
            pbScoreTree.Size = new Size(TileSize, TileSize * 3);

            temp = new ObCloud(ObCloud.CLOUD.Cloud1);
            pbCloud1.Image = temp.GetBitmap();
            pbCloud1.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObCloud(ObCloud.CLOUD.Cloud2);
            pbCloud2.Image = temp.GetBitmap();
            pbCloud2.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObGrass(ObGrass.GRASS.Grass);
            pbGrass.Image = temp.GetBitmap();
            pbGrass.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObMountain(ObMountain.MOUNTAIN.Mountain_Big);
            pbMountainBig.Image = temp.GetBitmap();
            pbMountainBig.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObMountain(ObMountain.MOUNTAIN.Mountain_Small);
            pbMountainSmall.Image = temp.GetBitmap();
            pbMountainSmall.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObCastle(ObCastle.Castle.Big);
            pbCastleBig.Image = temp.GetBitmap();
            pbCastleBig.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObCastle(ObCastle.Castle.Small);
            pbCastleSmall.Image = temp.GetBitmap();
            pbCastleSmall.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObPipe(ObPipe.PIPE.Down);
            pbPileDown.Image = temp.GetBitmap();
            pbPileDown.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObPipe(ObPipe.PIPE.Up);
            pbPipeUp.Image = temp.GetBitmap();
            pbPipeUp.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObPipe(ObPipe.PIPE.Left);
            pbPipeLeft.Image = temp.GetBitmap();
            pbPipeLeft.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObPipe(ObPipe.PIPE.Right);
            pbPipeRight.Image = temp.GetBitmap();
            pbPipeRight.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObPipe(ObPipe.PIPE.MidHor);
            pbPipeMidHor.Image = temp.GetBitmap();
            pbPipeMidHor.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObPipe(ObPipe.PIPE.MidVer);
            pbPipeMidVer.Image = temp.GetBitmap();
            pbPipeMidVer.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObAcMario(ObActive.INFO.RIGHT);
            pbMario.Image = temp.GetBitmap();
            pbMario.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObAcEnemy1();
            pbEnermy1.Image = Properties.Resources.Enermy1;
            pbEnermy1.Size = new Size(4 * TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObAcEnemy2();
            pbEnermy2.Image = Properties.Resources.Enermy2;
            pbEnermy2.Size = new Size(3 * TileSize * temp.GetWidth(), 3 * TileSize * temp.GetHeight());

            temp = new ObDeathField();
            pbDeathField.Image = Properties.Resources.DeathField;
            pbDeathField.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());

            temp = new ObCoin();
            pbCoin.Image = Properties.Resources.Coin;
            pbCoin.Size = new Size(TileSize * temp.GetWidth(), TileSize * temp.GetHeight());
            //=================================

            CreatePb(MapMaxHeight, MapMaxWitdh);
        }

        private void MenuNewMapClick(object sender, EventArgs e)
        {
            CreatePb(MapMaxHeight,MapMaxWitdh);
        }

        private void MenuExitClick(object sender, EventArgs e)
        {
            Application.Exit();
        }

        public void CreatePb(int row, int col)
        {
            _row = row;
            _col = col;

            pbMain.Image = null;
            _fileName = "";
            menuSave.Enabled    = true;
            menuSaveAs.Enabled  = true;
            
            DrawPbGrid(col, row);
            pbMain.Visible      = true;
            tabObject.Visible   = true;
            _chosedMap           = new List<ObObject>();
            _backgroundMap       = new List<ObObject>();

            _addObject.Dispose();
            _selectedObject.Dispose();
            Text = "MarioMapEditor - Tệp mới";
        }

        public void CreateBlackground(int info)
        {
            _addObject = new ObBlackground();
            _addObject.SetID(info);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();     
        }

        public void DrawPbGrid(int col, int row)
        {
            if(pbMain.Image == null)
            {
                var bmp = new Bitmap(col * TileSize + 1, row * TileSize + 1);
                pbMain.Image = bmp;
                pbMain.Size = new Size(col * TileSize + 1, row * TileSize + 1);

                var p = new Pen(Color.White, 1);

                using (var gdraw = Graphics.FromImage(pbMain.Image))
                {
                    gdraw.Clear(pbMain.BackColor);
                    for (var i = 0; i <= col; i++)
                    {
                        gdraw.DrawLine(p, i * TileSize, 0, i * TileSize, row * TileSize);
                    }

                    for (var j = 0; j <= row; j++)
                    {
                        gdraw.DrawLine(p, 0, j * TileSize, col * TileSize, j * TileSize);
                    }
                }
                
            }

            if(pbStuff.Image == null)
            {
                var bmp = new Bitmap(col * TileSize + 1, row * TileSize + 1);
                pbStuff.Image = bmp;
                pbStuff.Size = new Size(col * TileSize + 1, row * TileSize + 1);
                _g = Graphics.FromImage(pbStuff.Image);
            }
            
        }

        private void PbBrickMouseDown(object sender, MouseEventArgs e)
        {
            var x = e.X / TileSize;
            var y = e.Y / TileSize;
            var id = ObBrick.selectBrick(x, y);
            _addObject = new ObBrick();
            if (!_addObject.SetID(id)) return;
            pbMouse.Image = _addObject.GetBitmap();
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
        }

        private void PbBrickSpMouseDown(object sender, MouseEventArgs e)
        {
            var x = e.X / TileSize;
            var y = e.Y / TileSize;
            var id = ObBrickSP.selectBrick(x, y);
            if (id == 0)
            {
                return;
            }
            var form = new FormBrickSP(this, id);
            form.ShowDialog();
        }

        public void CreateBrickSp(int id, int info)
        {
            if (id == 0) return;
            _addObject = new ObBrickSP();
            _addObject.SetID(id);
            _addObject.SetINFO(info);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void PbMouseMouseMove(object sender, MouseEventArgs e)
        {
            var x = pbMouse.Location.X + e.X + panel1.HorizontalScroll.Value;
            var y = pbMouse.Location.Y + e.Y + panel1.VerticalScroll.Value;

            if (btGrid.Checked)
            {
                if (x % TileSize < 3 || y % TileSize < 3)
                {
                    return;
                }
            }

            LocatePbMouse(x, y);
        }

        private void LocatePbMouse(int paramx, int paramy)
        {
            if (_addObject.GetID() != 0)
            {
                pbMouse.Visible = true;

                var rootx = paramx / TileSize;
                var rooty = paramy / TileSize;

                var dx = paramx - panel1.HorizontalScroll.Value;
                var dy = paramy - panel1.VerticalScroll.Value;

                var tempx = -panel1.HorizontalScroll.Value % TileSize;
                var tempy = -panel1.VerticalScroll.Value % TileSize;
                var x = (dx - tempx) / TileSize;
                var y = (dy - tempy) / TileSize;
                y++;

                if (paramx >= 0 && paramy >= 0 
                    && paramx < _col*TileSize
                    && paramy < _row*TileSize)
                {
                    if (btGrid.Checked)
                    {
                        pbMouse.Location = new Point(x * TileSize + 3 + tempx, (y -1) * TileSize + 3 + tempy);
                        _addObject.SetX(rootx*TileSize);
                        _addObject.SetY((_row - rooty)*TileSize);
                    }
                    else
                    {
                        pbMouse.Location = new Point(dx - _addObject.GetHeight() * TileSize/2, dy - _addObject.GetHeight() * TileSize/2);
                        pbMouse.Invalidate();
                        _addObject.SetX(paramx - _addObject.GetHeight() * TileSize / 2 - 3);
                        _addObject.SetY(_row *TileSize - (paramy - _addObject.GetHeight() * TileSize / 2 - 3));
                    }

                    tbInfo.Text = "Vị trí hiện tại = (" + (_addObject.GetX()) + ";" + (_addObject.GetY()) + ")";
                }
            }
            else
            {
                pbMouse.Visible = false;
            }
        }

        private void PbMouseMouseDown(object sender, MouseEventArgs e)
        {
            switch (e.Button)
            {
                case MouseButtons.Right:
                    if (_addObject.GetID() != 0)
                    {
                        _addObject.Dispose();
                        pbMouse.Visible = false;
                        tbInfo.Text = "";
                    }
                    break;
                case MouseButtons.Left:
                    {
                        if (_addObject.GetID() == 0)
                        {
                            return;
                        }
                        AABB ob1;
                        ob1.left = _addObject.GetX();
                        ob1.top = _addObject.GetY();
                        ob1.right = ob1.left + _addObject.GetWidth() * TileSize;
                        ob1.bot = ob1.top - _addObject.GetHeight() * TileSize;

                        if (!IsBackground(_addObject))
                        {
                            if (checkMapPosition(_chosedMap,ob1).GetID() == 0)
                            {
                                _chosedMap.Add((ObObject)_addObject.Clone());
                                RepaintAll(true);
                            }
                            else
                            {
                                tbInfo.Text = ("Không thể đặt vật thể vào vị trí này...");
                            }
                        }
                        else
                        {
                            if (checkMapPosition(_backgroundMap, ob1).GetID() == 0)
                            {
                                _backgroundMap.Add((ObObject)_addObject.Clone());
                                RepaintAll(true);
                            }
                            else
                            {
                                tbInfo.Text = ("Không thể đặt vật thể vào vị trí này...");
                            }
                        }
                    }
                    break;
            }
        }

        private ObObject checkMapPosition(IList<ObObject> map, AABB needRect)
        {
            if (map == null) return null;
            for (var i = (map.Count -1); i >= 0; i--)
            {
                var current = map[i].GetID();
                if (current == 0) continue;
                AABB ob1;
                ob1.left = map[i].GetX();
                ob1.top =  map[i].GetY();
                ob1.right = ob1.left + map[i].GetWidth() * TileSize;
                ob1.bot = ob1.top - map[i].GetHeight() * TileSize;
                if (TreeClipping.checkCollision(ob1, needRect))
                {
                    return map[i];
                }
            }
            var temp = new ObObject();
            temp.Dispose();
            return temp;
        }

        private static bool IsBackground(ObObject checkob)
        {
            //==========================================
            if (checkob is ObBrick || checkob is ObPipe || checkob is ObBrickSP || 
                checkob is ObAcMario || checkob is ObAcEnemy1 || checkob is ObAcEnemy2  
                || checkob is ObFlagTree || checkob is ObDeathField || checkob is ObCoin)
            {
                return false;
            }
            return true;
            //==========================================
        }

        private AABB currentAABB;

        private void RepaintAll(bool force)
        {
            DrawPbGrid(_col, _row);
            AABB needDraw;
            //800x600
            var y = panel1.VerticalScroll.Value / 600;
            var x = panel1.HorizontalScroll.Value / 800;
            needDraw.left = x * 800;
            needDraw.right = ((panel1.HorizontalScroll.Value + panel1.Width)/800 + 1)*800;

            needDraw.top = y * 600;
            needDraw.bot = ((panel1.VerticalScroll.Value + panel1.Height)/600 + 1)*600 ;
            
            if(force == false)
            {
                if (needDraw.left == currentAABB.left && needDraw.top == currentAABB.top &&
                needDraw.right == currentAABB.right && needDraw.bot == currentAABB.bot) return;
            }

            currentAABB.left = needDraw.left;
            currentAABB.right = needDraw.right;
            currentAABB.top = needDraw.top;
            currentAABB.bot = needDraw.bot;

            _g.Clear(pbStuff.BackColor);
            RepaintBackground();
            RepaintChosedMap();
            RepaintHighlight();
            pbMain.Invalidate();
        }

        private void RepaintHighlight()
        {
            if (_selectedObject.GetID() == 0) return;

            var rx = _selectedObject.GetX();
            var ry = _selectedObject.GetY();
            ry = _row * TileSize - ry;
            var rect = new Rectangle(rx, ry, 
                                        _selectedObject.GetWidth() * TileSize, _selectedObject.GetHeight() * TileSize);
            using (var pen = new Pen(Color.OrangeRed, 2))
            {
                _g.DrawRectangle(pen, rect);
                //===============================
                if (_selectedObject.GetINFO() == 0) return;
                var strshow = "";
                if (_selectedObject is ObPipe)
                {
                    strshow = (_selectedObject.GetINFO()).ToString(CultureInfo.InvariantCulture);
                }
                else if (_selectedObject is ObBrickSP)
                {
                    strshow = ((ObBrickSP.INFO)_selectedObject.GetINFO()).ToString();
                }
                else if (Enum.IsDefined(typeof(ObActive.INFO),_selectedObject.GetINFO()))
                {
                    strshow = ((ObActive.INFO)_selectedObject.GetINFO()).ToString();
                }
                else if (Enum.IsDefined(typeof(ObAcEnemy1.RUAINFO), _selectedObject.GetINFO()))
                {
                    strshow = ((ObAcEnemy1.RUAINFO)_selectedObject.GetINFO()).ToString();
                }

                var a = new Font(Font, FontStyle.Bold);
                _g.DrawString(strshow, a, Brushes.Yellow,
                                rx + 5, ry + 5);
                //===============================
            }
        }

        private static bool checkCollisioninvertedY(AABB OB1, AABB OB2)
        {
            if (OB1.bot <= OB2.top) return false;
            if (OB2.bot <= OB1.top) return false;
            if (OB1.left >= OB2.right) return false;
            if (OB2.left >= OB1.right) return false;
            return true;
        }

        private void RepaintBackground()
        {
            for (var i = (_backgroundMap.Count-1); i>=0 ; i--)
            {
                if (_backgroundMap[i] == null) continue;
                if (_backgroundMap[i].GetID() == 0) continue;
                if (!IsBackground(_backgroundMap[i])) continue;

                var rx = _backgroundMap[i].GetX();
                var ry = _backgroundMap[i].GetY();
                ry = _row*TileSize - ry;
                //check
                AABB ab;
                ab.left = rx;
                ab.right = rx + _backgroundMap[i].GetWidth()*TileSize;
                ab.top = ry;
                ab.bot = ry+ _backgroundMap[i].GetHeight()*TileSize;
                if (!checkCollisioninvertedY(currentAABB, ab))
                {
                    continue;
                }  

                _g.DrawImage(_backgroundMap[i].GetBitmap(), new Rectangle(rx, ry, _backgroundMap[i].GetWidth() * TileSize, _backgroundMap[i].GetHeight() * TileSize));
                
            }
        }

        private void RepaintChosedMap()
        {
            foreach (var t in _chosedMap)
            {
                if (t == null) continue;
                if (t.GetID() == 0) continue;
                if (IsBackground(t)) continue;
                var rx = t.GetX();
                var ry = t.GetY();
                ry = _row * TileSize - ry;

                //check
                AABB ab;
                ab.left = rx;
                ab.right = rx + t.GetWidth() * TileSize;
                ab.top = ry;
                ab.bot = ry + t.GetHeight() * TileSize;
                if (!checkCollisioninvertedY(currentAABB, ab))
                {
                    continue;
                }  
                _g.DrawImage(t.GetBitmap(), new Rectangle(rx, ry, t.GetWidth() * TileSize, t.GetHeight() * TileSize));
                
            }
        }

        private void PbStuffMouseDown(object sender, MouseEventArgs e)
        {
            AABB a;
            a.left = e.X;
            a.top = _row * TileSize - e.Y;
            a.bot = a.top - 1;
            a.right = a.left + 1;

            _selectedObject = checkMapPosition(_chosedMap, a);
            if (_selectedObject.GetID() != 0)
            {
                tbInfo.Text = "ID :" + _selectedObject.GetID();
            }
            else
            {
                _selectedObject = checkMapPosition(_backgroundMap, a);
                if (_selectedObject.GetID() != 0)
                {
                    tbInfo.Text = "ID :" + _selectedObject.GetID();
                }
                else
                {
                    tbInfo.Text = "";
                }
            }
            if (_selectedObject.GetID() != 0)
            {
                if (e.Button == MouseButtons.Right)
                {
                    menuc.Show(pbMain, e.Location);
                }
            }
            CheckMenu();
            RepaintAll(true);
        }

        private void PbStuffMouseMove(object sender, MouseEventArgs e)
        {
            LocatePbMouse(e.X, e.Y);
        }

        private void MenuCopyClick(object sender, EventArgs e)
        {
            Copy();
        }

        private void Copy()
        {
            if (_selectedObject.GetID() == 0) return;
            _addObject = (ObObject) _selectedObject.Clone();
            pbMouse.Image = _addObject.GetBitmap();
            pbMouse.Size = new Size(_addObject.GetWidth() * TileSize, 
                                                   _selectedObject.GetHeight() * TileSize);
            CheckMenu();
            RepaintAll(false);
        }

        private void Cut()
        {
            if (_selectedObject.GetID() == 0) return;

            _addObject = (ObObject)_selectedObject.Clone();
            _selectedObject.Dispose();
            _chosedMap.Remove(_selectedObject);
            _backgroundMap.Remove(_selectedObject);
                
            pbMouse.Image = _addObject.GetBitmap();
            pbMouse.Size = new Size(_addObject.GetWidth()*TileSize, _addObject.GetHeight()*TileSize);
            CheckMenu();
            RepaintAll(true);
        }

        private void Delete()
        {
            if (_selectedObject.GetID() == 0) return;
            _selectedObject.Dispose();
            _chosedMap.Remove(_selectedObject);
            _backgroundMap.Remove(_selectedObject);
            CheckMenu();
            RepaintAll(true);
        }

        private void MenuMoveClick(object sender, EventArgs e)
        {
            Cut();
        }

        private void MenuDeleteClick(object sender, EventArgs e)
        {
            Delete();
        }

        private void CheckMenu()
        {
            menuEdit.Visible = _selectedObject.GetID() != 0;
        }

        private void MenucCopyClick(object sender, EventArgs e)
        {
            Copy();
        }

        private void MenucMoveClick(object sender, EventArgs e)
        {
            Cut();
        }

        private void MenucDeleteClick(object sender, EventArgs e)
        {
            Delete();
        }

        private void MenuSaveClick(object sender, EventArgs e)
        {
            if (_fileName.Equals(""))
            {
                MenuSaveAsClick(null, null);
            }
            else
            {
                Savefile(_fileName);
            }
            
        }

        private static ObObject GetObjectFromObjectID(int objectID)
        {
            ObObject temp = null;
            //==========================================
            if (ObBrick.checkType(objectID))
            {
                temp = new ObBrick();
                temp.SetID(objectID);
            }
            else if (ObCloud.checkType(objectID))
            {
                temp = new ObCloud();
                temp.SetID(objectID);
            }
            else if (ObGrass.checkType(objectID))
            {
                temp = new ObGrass();
                temp.SetID(objectID);
            }
            else if (ObMountain.checkType(objectID))
            {
                temp = new ObMountain();
                temp.SetID(objectID);
            }
            else if (ObCastle.checkType(objectID))
            {
                temp = new ObCastle();
                temp.SetID(objectID);
            }
            else if (ObPipe.checkType(objectID))
            {
                temp = new ObPipe();
                temp.SetID(objectID);
            }
            else if (ObBrickSP.checkType(objectID))
            {
                temp = new ObBrickSP();
                temp.SetID(objectID);
            }
            else if (ObAcMario.checkType(objectID))
            {
                temp = new ObAcMario();
                temp.SetID(objectID);
            }
            else if (ObAcEnemy1.checkType(objectID))
            {
                temp = new ObAcEnemy1();
                temp.SetID(objectID);
            }
            else if (ObAcEnemy2.checkType(objectID))
            {
                temp = new ObAcEnemy2();
                temp.SetID(objectID);
            }
            else if (ObBlackground.checkType(objectID))
            {
                temp = new ObBlackground();
                temp.SetID(objectID);
            }
            else if (ObFlagTree.checkType(objectID))
            {
                temp = new ObFlagTree();
                temp.SetID(objectID);
            }
            else if (ObDeathField.checkType(objectID))
            {
                temp = new ObDeathField();
                temp.SetID(objectID);
            }
            else if (ObCoin.checkType(objectID))
            {
                temp = new ObCoin();
                temp.SetID(objectID);
            }
            //==========================================
            return temp;
        }

        private void MenuOpenClick(object sender, EventArgs e)
        {
            
            var open = new OpenFileDialog
                           {
                               Title = "Mở Map",
                               Filter = "Tập tin xdmap | *.xdmap",
                               InitialDirectory = System.IO.Path.GetDirectoryName(Application.ExecutablePath)
                           };
            if (open.ShowDialog() == DialogResult.OK)
            {
                var bGEND = false;
                var sr = new System.IO.StreamReader(open.FileName);
                CreatePb(MapMaxHeight, MapMaxWitdh);
                _fileName = open.FileName;

                _chosedMap = new List<ObObject>();
                _backgroundMap = new List<ObObject>();

                try
                {
                    string line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        if (line.Equals("END"))
                        {
                            bGEND = true;
                        }
                        else if (line.Equals("QUAD_BACK"))
                        {
                            break;
                        }
                        else
                        {
                            var param = line.Split(' ').Select(n => Convert.ToInt32(n)).ToArray();
                            if (param.Length >= 4)
                            {
                                if (bGEND)
                                {
                                    var temp = GetObjectFromObjectID(param[2]);
                                    if (temp != null)
                                    {
                                        temp.SetINFO(param[3]);
                                        var left = param[0] - temp.GetWidth() * TileSize / 2;
                                        var top = param[1] + temp.GetHeight() * TileSize / 2;
                                        temp.SetX(left);
                                        temp.SetY(top);
                                        _chosedMap.Add(temp);
                                    }
                                }
                                else
                                {
                                    var temp = GetObjectFromObjectID(param[2]);
                                    if (temp != null)
                                    {
                                        temp.SetINFO(param[3]);
                                        var left = param[0] - temp.GetWidth() * TileSize / 2;
                                        var top = param[1] + temp.GetHeight() * TileSize / 2;
                                        temp.SetX(left);
                                        temp.SetY(top);
                                        _backgroundMap.Add(temp);
                                    }
                                }
                            }
                        }
                    }
                    sr.Close();
                    sr.Dispose();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error");
                    CreatePb(MapMaxHeight, MapMaxWitdh);
                    return;
                    
                }
                RepaintAll(true);
                Text = "MarioMapEditor - " + System.IO.Path.GetFileName(_fileName);
            }
        }

        private void MenuSaveAsClick(object sender, EventArgs e)
        {
            var save = new SaveFileDialog
                           {
                               Title = "Lưu Map",
                               Filter = "Tập tin xdmap | *.xdmap",
                               InitialDirectory = System.IO.Path.GetDirectoryName(Application.ExecutablePath)
                           };
            if (save.ShowDialog() == DialogResult.OK)
            {
                _fileName = save.FileName;
                Savefile(save.FileName);
            }
        }

        private void Savefile(string name)
        {
            var rootNode = BuildQuadTree(0);
            var backNode = BuildQuadTree(1);
            try
            {
                var sw = new System.IO.StreamWriter(name);
                foreach (var t in _backgroundMap)
                {
                    if (t == null) continue;
                    if (t.GetID() == 0) continue;

                    var centerx = t.GetX() + t.GetWidth() * TileSize / 2;
                    var centery = t.GetY() - t.GetHeight() * TileSize / 2;
                    sw.WriteLine(centerx + " " + centery + " " + t.GetID() + " " + t.GetINFO() + " " + t.GetHashId());
                }

                sw.WriteLine("END");
                foreach (var t in _chosedMap)
                {
                    if (t == null) continue;
                    if (t.GetID() == 0) continue;

                    var centerx = t.GetX() + t.GetWidth() * TileSize / 2;
                    var centery = t.GetY() - t.GetHeight() * TileSize / 2;
                    sw.WriteLine(centerx + " " + centery + " " + t.GetID() + " " + t.GetINFO() + " " + t.GetHashId());
                }
                sw.WriteLine("QUAD_BACK");
                Export(sw, backNode);
                sw.WriteLine("QUAD_CHOSED");
                Export(sw, rootNode);
                sw.Dispose();
                sw.Close();

                MessageBox.Show("Lưu map thành công!");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            Text = "MarioMapEditor - " + System.IO.Path.GetFileName(_fileName);
        }

        private static void Export(System.IO.StreamWriter sw, TreeNode node)
        {
            if(node == null) return;
            var temp = "";
            for(var i = 0; i <node.listObjects.Count;i++){
                temp += " ";
                temp +=node.listObjects[i].target.GetHashId();
            }
            sw.WriteLine(node.id + " " + node.pos.left + " " + node.pos.top + " " + node.pos.right + " " + node.pos.bot + temp);
            Export(sw, node.northWest);
            Export(sw, node.northEast);
            Export(sw, node.southEast);
            Export(sw, node.southWest);
        }

        private TreeNode BuildQuadTree(int mapType)
        {
            switch (mapType)
            {
                case 0:
                    SetHashID(mapType);
                    var rootAABB = CalChosedRECT();
                    if (rootAABB.left == 0 && rootAABB.right == 0 && rootAABB.top == 0 && rootAABB.bot == 0) return null;
                    var rootNode = new TreeNode(rootAABB.left, rootAABB.top, rootAABB.right, rootAABB.bot)
                                       {listObjects = CalMovingMap(rootAABB), id = "1"};
                    rootNode.build();
                    return rootNode;
                case 1:
                    SetHashID(mapType);
                    var backAABB = CalBackRECT();
                    if (backAABB.left == 0 && backAABB.right == 0 && backAABB.top == 0 && backAABB.bot == 0) return null;
                    var backNode = new TreeNode(backAABB.left, backAABB.top, backAABB.right, backAABB.bot)
                                       {listObjects = CalBackgroundMap(), id = "1"};
                    backNode.build();
                    return backNode;
                default:
                    return null;
            }
        }

        private void SetHashID(int mapType)
        {
            if(mapType == 1)
            {
                var count = 1;
                foreach (var t in _backgroundMap)
                {
                    if (t == null) continue;
                    t.SetHashId(count);
                    count++;
                }
            }
            else
            {
                var count = 1;
                foreach (var t in _chosedMap)
                {
                    if (t == null) continue;
                    t.SetHashId(count);
                    count++;
                }
            }
        }

        private List<TreeObject> CalMovingMap(AABB rect)
        {
            var movingMap = new List<TreeObject>();

            for (var i = 0; i < _chosedMap.Count; i++)
            {
                if (_chosedMap[i].GetID() == 0) continue;

                //set target
                var ob = new TreeObject {target = _chosedMap[i]};

                if (_chosedMap[i] is ObAcEnemy1 || _chosedMap[i] is ObAcEnemy2)
                {
                    ob.pos = CalMovingArea(rect, i, true, false);
                }else if (_chosedMap[i] is ObAcMario)
                {
                    continue;
                }else //(_chosedMap[i] is ObBrick || _chosedMap[i] is ObBrickSP || _chosedMap[i] is ObPipe)
                {
                    ob.pos = CalMovingArea(rect, i, false, false);
                }

                movingMap.Add(ob);
            }
            return movingMap;
        }

        private List<TreeObject> CalBackgroundMap()
        {
            var movingMap = new List<TreeObject>();
            foreach (var t in _backgroundMap)
            {
                if (t.GetID() == 0) continue;
                //set target
                var ob = new TreeObject {target = t, pos = {left = t.GetX(), top = t.GetY()}};
                ob.pos.right = ob.pos.left + t.GetWidth() * TileSize;
                ob.pos.bot = ob.pos.top - t.GetHeight() * TileSize;
                movingMap.Add(ob);
            }
            return movingMap;
        }

        private bool CheckMovingCollision(AABB rect)
        {
            foreach (var t in _chosedMap)
            {
                if (t is ObBrick || t is ObBrickSP || t is ObPipe)
                {
                    AABB ab;
                    ab.left = t.GetX();
                    ab.top = t.GetY();
                    ab.right = t.GetX() + t.GetWidth()*TileSize;
                    ab.bot = t.GetY() - t.GetHeight()*TileSize;
                    if (TreeClipping.checkCollision(rect, ab))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        private AABB CalMovingArea(AABB rect, int pos, bool isLeftRight, bool isUpdown)
        {
            AABB rs;
            rs.left = _chosedMap[pos].GetX();
            rs.top = _chosedMap[pos].GetY();
            rs.right = rs.left + _chosedMap[pos].GetWidth()*TileSize;
            rs.bot = rs.top - _chosedMap[pos].GetHeight()*TileSize;

            if (isLeftRight)
            {
                for (var left = rs.left; left > rect.left; left-=TileSize)
                {
                    if (CheckMovingCollision(rs))
                    {
                        break;
                    }
                    rs.left = left;
                }

                for (var right = rs.right; right < rect.right; right+=TileSize)
                {
                    if (CheckMovingCollision(rs))
                    {
                        break;
                    }
                    rs.right = right;
                }
            }

            if (isUpdown)
            {
                for (var top = rs.top; top < rect.top; top += TileSize)
                {
                    if (CheckMovingCollision(rs))
                    {
                        break;
                    }
                    rs.top = top;
                }

                for (var bot = rs.bot; bot > rect.bot; bot -= TileSize)
                {
                    if (CheckMovingCollision(rs))
                    {
                        break;
                    }
                    rs.bot = bot;
                }
            }

            return rs;
        }

        private AABB CalChosedRECT()
        {
            AABB rs;
            rs.left = 0;
            rs.right = 0;
            rs.top = 0;
            rs.bot = 0;
            if (_chosedMap.Count == 0) return rs;
            rs.left = _chosedMap[0].GetX();
            rs.top = _chosedMap[0].GetY();
            rs.right = _chosedMap[0].GetX() + _chosedMap[0].GetWidth() * TileSize;
            rs.bot = _chosedMap[0].GetY() - _chosedMap[0].GetHeight() * TileSize;

            foreach (var t in _chosedMap)
            {
                if (t.GetX() < rs.left)
                {
                    rs.left = t.GetX();
                }

                if ((t.GetX() + t.GetWidth()*TileSize) > rs.right)
                {
                    rs.right = (t.GetX() + t.GetWidth() * TileSize);
                }

                if (t.GetY() > rs.top)
                {
                    rs.top = t.GetY();
                }

                if ((t.GetY() - t.GetHeight()*TileSize) < rs.bot)
                {
                    rs.bot = (t.GetY() - t.GetHeight() * TileSize);
                }
            }

            var w = rs.right - rs.left;
            var h = rs.top - rs.bot;
            if (w > h)
            {
                rs.top = rs.bot + w;
            }
            else
            {
                rs.right = rs.left + h;
            }

            return rs;
        }

        private AABB CalBackRECT()
        {
            AABB rs;
            rs.left = 0;
            rs.right = 0;
            rs.top = 0;
            rs.bot = 0;
            if (_backgroundMap.Count == 0) return rs;
            rs.left = _backgroundMap[0].GetX();
            rs.top = _backgroundMap[0].GetY();
            rs.right = _backgroundMap[0].GetX() + _backgroundMap[0].GetWidth() * TileSize;
            rs.bot = _backgroundMap[0].GetY() - _backgroundMap[0].GetHeight() * TileSize;

            foreach (var t in _backgroundMap)
            {
                if (t.GetX() < rs.left)
                {
                    rs.left = t.GetX();
                }

                if ((t.GetX() + t.GetWidth() * TileSize) > rs.right)
                {
                    rs.right = (t.GetX() + t.GetWidth() * TileSize);
                }

                if (t.GetY() > rs.top)
                {
                    rs.top = t.GetY();
                }

                if ((t.GetY() - t.GetHeight() * TileSize) < rs.bot)
                {
                    rs.bot = (t.GetY() - t.GetHeight() * TileSize);
                }
            }

            var w = rs.right - rs.left;
            var h = rs.top - rs.bot;
            if (w > h)
            {
                rs.top = rs.bot + w;
            }
            else
            {
                rs.right = rs.left + h;
            }

            return rs;
        }

        public static Bitmap ResizeImage(Bitmap imgToResize, Size size)
        {
            var b = new Bitmap(size.Width, size.Height);
            using (var g = Graphics.FromImage(b))
            {
                g.DrawImage(imgToResize, 0, 0, size.Width, size.Height);
            }
            return b;
        }

        private void PbCloud1MouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObCloud(ObCloud.CLOUD.Cloud1);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void PbCloud2MouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObCloud(ObCloud.CLOUD.Cloud2);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void PbGrassMouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObGrass(ObGrass.GRASS.Grass);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void PbMountainBigMouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObMountain(ObMountain.MOUNTAIN.Mountain_Big);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void PbMountainSmallMouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObMountain(ObMountain.MOUNTAIN.Mountain_Small);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void PbCastleBigMouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObCastle(ObCastle.Castle.Big);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();        
        }

        private void PbCastleSmallMouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObCastle(ObCastle.Castle.Small);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void PbPipeUpMouseDown(object sender, MouseEventArgs e)
        {
            var form = new FormPipe(this, (int)ObPipe.PIPE.Up);
            form.ShowDialog();
        }

        public void CreatePipe(int id, int info)
        {
            if (id == 0) return;
            _addObject = new ObPipe();
            _addObject.SetID(id);
            _addObject.SetINFO(info);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        public void CreateMapFromObjects()
        {

        }

        public void CreateActiveOb(int id, int info)
        {
            if (id == 0) return;
            //============================
            if (ObAcMario.checkType(id))
            {
                _addObject = new ObAcMario();
                _addObject.SetID(id);
                _addObject.SetINFO(info);
                pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
                pbMouse.Image = _addObject.GetBitmap();
            }
            else if (ObAcEnemy1.checkType(id))
            {
                _addObject = new ObAcEnemy1();
                _addObject.SetID(id);
                _addObject.SetINFO(info);
                pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
                pbMouse.Image = _addObject.GetBitmap();
            }
            else if (ObAcEnemy2.checkType(id))
            {
                _addObject = new ObAcEnemy2();
                _addObject.SetID(id);
                _addObject.SetINFO(info);
                pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
                pbMouse.Image = _addObject.GetBitmap();
            }
            //============================
        }

        private void PbPileDownMouseDown(object sender, MouseEventArgs e)
        {
            var form = new FormPipe(this, (int)ObPipe.PIPE.Down);
            form.ShowDialog();
        }

        private void PbPipeLeftMouseDown(object sender, MouseEventArgs e)
        {
            var form = new FormPipe(this, (int)ObPipe.PIPE.Left);
            form.ShowDialog();
        }

        private void PbPipeRightMouseDown(object sender, MouseEventArgs e)
        {
            var form = new FormPipe(this, (int)ObPipe.PIPE.Right);
            form.ShowDialog();
        }

        private void PbPipeMidVerMouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObPipe(ObPipe.PIPE.MidVer);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void PbPipeMidHorMouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObPipe(ObPipe.PIPE.MidHor);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void PbMarioMouseDown(object sender, MouseEventArgs e)
        {
            var form = new FormActive(this, (int)ObAcMario.MARIO.MARIO, (int)ObActive.INFO.RIGHT);
            form.ShowDialog();
        }

        private void PbEnermy1MouseDown(object sender, MouseEventArgs e)
        {
            var xpos = e.X / TileSize;
            var id = ObAcEnemy1.selectEnermy(xpos);
            if (id == (int)ObAcEnemy1.ENERMY1.RUAXANH || id == (int)ObAcEnemy1.ENERMY1.RUADO)
            {
                var form = new FormRua(this, id);
                form.ShowDialog();
            }
            else
            {
                var form = new FormActive(this, id, (int)ObActive.INFO.LEFT);
                form.ShowDialog();
            }
        }

        private void PbEnermy2MouseDown(object sender, MouseEventArgs e)
        {
            var xpos = e.X / TileSize;
            var ypos = e.Y / TileSize;
            var id = ObAcEnemy2.selectEnermy(xpos, ypos);

            if (id == 0) return;
            var form = new FormActive(this, id, (int)ObActive.INFO.LEFT);
            form.ShowDialog();
        }

        private List<ObObject> CopyArray(IList<ObObject> src)
        {
            var tar = new List<ObObject>();
            for (var i = 0; i < src.Count; i++)
            {
                if (src[i] == null)
                {
                    continue;
                }
                tar.Add((ObObject)src[i].Clone());
            }
            return tar;
        }

        private void PbScoreTreeMouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObFlagTree(ObFlagTree.FLAG.FLAGTREE);
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void PbBlackgroundMouseDown(object sender, MouseEventArgs e)
        {
            var form = new FormBlackground(this);
            form.ShowDialog();
        }

        private void PbDeathFieldMouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObDeathField();
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

        private void panel1_Scroll(object sender, ScrollEventArgs e)
        {
            RepaintAll(false);

        }

        private void pbCoin_MouseDown(object sender, MouseEventArgs e)
        {
            _addObject = new ObCoin();
            pbMouse.Size = new Size(TileSize * _addObject.GetWidth(), TileSize * _addObject.GetHeight());
            pbMouse.Image = _addObject.GetBitmap();
        }

    }
}
